package org.yu.api.util;

import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.WritableRegistry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.data.worldgen.ProcessorLists;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.network.NetworkHooks;
import org.yu.mixin.accessors.SingleJigsawAccess;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.OptionalInt;
import java.util.Random;
import java.util.stream.Collectors;

public class LazyUtil {
    private static final BlockEntityTicker<? extends BlockEntity> TICKER = (pLevel, pPos, pState, pBlockEntity) -> {
        if (pBlockEntity instanceof ITickable ITickable) ITickable.tick();
    };

    /**
     * 打开菜单（不传递坐标信息）
     *
     * @param player 玩家
     * @param menu   菜单类型
     * @param title  标题
     * @param pos    方块位置
     */
    public static void openMenu(ServerPlayer player, MenuType<?> menu, Component title, BlockPos pos) {
        MenuProvider containerProvider = new MenuProvider() {

            @Nonnull
            @Override
            public Component getDisplayName() {
                return title;
            }

            @Override
            public AbstractContainerMenu createMenu(int containerId, @Nonnull Inventory inventory, @Nonnull Player player) {
                FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
                return menu.create(containerId, inventory, buffer);
            }
        };
        NetworkHooks.openGui(player, containerProvider, pos);
    }

    public static void translateTank(int amount, Fluid destType, IFluidTank source, IFluidTank dest, float rate) {
        if (source.getFluidAmount() < amount || dest.getFluidAmount() == dest.getCapacity())
            return;
        if (!dest.getFluid().isEmpty() && !dest.getFluid().getFluid().isSame(destType)) return;
        int drain = source.drain(amount, IFluidHandler.FluidAction.EXECUTE).getAmount();
        FluidStack fluidStack = new FluidStack(destType, (int) (drain * rate));
        dest.fill(fluidStack, IFluidHandler.FluidAction.EXECUTE);
    }

    @SuppressWarnings("unchecked")
    public static <T extends BlockEntity> BlockEntityTicker<T> getTicker() {
        return (BlockEntityTicker<T>) TICKER;
    }

    /**
     * 插入结构池
     * @param pool 池名称
     * @param toAdd 需要加入的nbt文件名
     * @param weight 权重
     */
    public static void addToPool(ResourceLocation pool, ResourceLocation toAdd, int weight)
    {
        StructureTemplatePool old = BuiltinRegistries.TEMPLATE_POOL.get(pool);
        int id = BuiltinRegistries.TEMPLATE_POOL.getId(old);

        // Fixed seed to prevent inconsistencies between different worlds
        List<StructurePoolElement> shuffled;
        if(old!=null)
            shuffled = old.getShuffledTemplates(new Random(0));
        else
            shuffled = ImmutableList.of();
        Object2IntMap<StructurePoolElement> newPieces = new Object2IntLinkedOpenHashMap<>();
        for(StructurePoolElement p : shuffled)
            newPieces.computeInt(p, (StructurePoolElement pTemp, Integer i) -> (i==null?0: i)+1);
        newPieces.put(SingleJigsawAccess.construct(
                Either.left(toAdd), ProcessorLists.EMPTY, StructureTemplatePool.Projection.RIGID
        ), weight);
        List<Pair<StructurePoolElement, Integer>> newPieceList = newPieces.object2IntEntrySet().stream()
                .map(e -> Pair.of(e.getKey(), e.getIntValue()))
                .collect(Collectors.toList());

        ResourceLocation name = old.getName();
        ((WritableRegistry<StructureTemplatePool>) BuiltinRegistries.TEMPLATE_POOL).registerOrOverride(
                OptionalInt.of(id),
                ResourceKey.create(BuiltinRegistries.TEMPLATE_POOL.key(), name),
                new StructureTemplatePool(pool, name, newPieceList),
                Lifecycle.stable()
        );
    }
}
